package uo.ri.bussiness.impl.cash;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import uo.ri.common.BusinessException;
import uo.ri.conf.PersistenceFactory;
import uo.ri.peristence.AveriaGateway;
import uo.ri.peristence.FacturaGateway;
import alb.util.date.DateUtil;
import alb.util.jdbc.Jdbc;
import alb.util.math.Round;

public class CreateInvoiceFor {
	
	private List<Long> idsAveria;
	private Connection connection;
	
	private FacturaGateway facturaGateway;
	private AveriaGateway averiaGateway;

	public CreateInvoiceFor(List<Long> idsAveria){
		this.idsAveria = idsAveria;
	}
	
	public Map<String,Object> execute() throws BusinessException {
		Map<String, Object> factura = null;
		try {
			
			prepareDB();
			
			verificarAveriasTerminadas(idsAveria);

			long numeroFactura = generarNuevoNumeroFactura();
			Date fechaFactura = DateUtil.today();
			double totalFactura = calcularImportesAverias(idsAveria);
			double iva = porcentajeIva(totalFactura, fechaFactura);
			double importe = totalFactura * (1 + iva/100);
			importe = Round.twoCents(importe);
			
			long idFactura = crearFactura(numeroFactura, fechaFactura, iva, importe);
			vincularAveriasConFactura(idFactura, idsAveria);
			cambiarEstadoAverias(idsAveria, "FACTURADA");
			
			connection.commit();
			
			//Creamos la factura que devolveremos
			factura = new HashMap<String, Object>();
			factura.put("numeroFactura", numeroFactura);
			factura.put("fechaFactura", fechaFactura);
			factura.put("totalFactura", totalFactura);
			factura.put("iva", iva);
			factura.put("importe", importe);
			factura.put("idFactura", idFactura);
		}
		catch (SQLException e) {
			try { connection.rollback(); } catch (SQLException ex) {};
			throw new RuntimeException(e);
		}
		catch (BusinessException e) {
			try { connection.rollback(); } catch (SQLException ex) {};
			throw e;
		}
		finally {
			Jdbc.close(connection);
		}
		return factura;
	}

	private void prepareDB() throws SQLException {
		connection = Jdbc.getConnection();
		connection.setAutoCommit(false);
		facturaGateway = PersistenceFactory.getFacturaGateway();
		averiaGateway = PersistenceFactory.getAveriaGateway();
		facturaGateway.setConnection(connection);
		averiaGateway.setConnection(connection);
		
	}
	
	private void verificarAveriasTerminadas(List<Long> idsAveria) throws SQLException, BusinessException {
			
		for (Long idAveria : idsAveria) {
			
			String status = averiaGateway.verificarEstadoAveria(idAveria);
			if (status == null) {
				throw new BusinessException("No existe la averia " + idAveria);
			}
			
			if (! "TERMINADA".equalsIgnoreCase(status) ) {
				throw new BusinessException("No está terminada la avería " + idAveria);
			}
		}

	}

	private void cambiarEstadoAverias(List<Long> idsAveria, String status) throws SQLException {

		for (Long idAveria : idsAveria) {
			averiaGateway.cambiarEstadoAveria(idAveria, status);
		}
		
	}

	private void vincularAveriasConFactura(long idFactura, List<Long> idsAveria) throws SQLException {
	
		for (Long idAveria : idsAveria) {
			averiaGateway.vincularAveriaFactura(idAveria, idFactura);
		}
	
	}

	private long crearFactura(long numeroFactura, Date fechaFactura,
			double iva, double totalConIva) throws SQLException {
		
		Map<String,Object> factura = new HashMap<String, Object>();
		factura.put("numeroFactura",numeroFactura);
		factura.put("fechaFactura",fechaFactura);
		factura.put("iva",iva);
		factura.put("totalConIva",totalConIva);
		
		Long idFactura = facturaGateway.save(factura);
		
		return idFactura;
	}


	private Long generarNuevoNumeroFactura() throws SQLException {
		
		Long lastInvoiceNumber = facturaGateway.getLastInvoiceNumber();
			
		if (lastInvoiceNumber != null) {
			return lastInvoiceNumber + 1; // +1, el siguiente
		} else {  // todavía no hay ninguna
			return 1L;
		}
		
	}

	private double porcentajeIva(double totalFactura, Date fechaFactura) {
		return DateUtil.fromString("1/7/2012").before(fechaFactura) ? 21.0 : 18.0;
	}

	protected double calcularImportesAverias(List<Long> idsAveria)
			throws BusinessException, SQLException {
		
		double totalFactura = 0.0;
		for(Long idAveria : idsAveria) {
			double importeManoObra = consultaImporteManoObra(idAveria);
			double importeRepuestos = consultaImporteRepuestos(idAveria);
			double totalAveria = importeManoObra + importeRepuestos;
			
			actualizarImporteAveria(idAveria, totalAveria);
			
			totalFactura += totalAveria; 
		}
		return totalFactura;
	}

	private void actualizarImporteAveria(Long idAveria, double totalAveria) throws SQLException {
		averiaGateway.actualizarImporteAveria(idAveria, totalAveria);
	}

	private double consultaImporteRepuestos(Long idAveria) throws SQLException {
		Double importeRepuestos = averiaGateway.consultaImporteRepuestos(idAveria);
		if (importeRepuestos == null) {
			return 0.0; // La averia puede no tener repuestos
		}
		
		return importeRepuestos;
		
	}

	private double consultaImporteManoObra(Long idAveria) throws BusinessException, SQLException {
		
		Double importeManoObra = averiaGateway.consultaImporteManoObra(idAveria);
		if (importeManoObra == null) {
			throw new BusinessException("La averia no existe o no se puede facturar");
		}
		
		return importeManoObra;
		
	}
	
	
	
	
}
